package core;

import static org.junit.Assert.assertEquals;

import java.io.IOException;

import net.sourceforge.peertwitter.core.PeerTwitterImpl;
import net.sourceforge.peertwitter.core.Profile;
import net.sourceforge.peertwitter.core.applications.communication.MBGroupCommunication;
import net.sourceforge.peertwitter.core.applications.communication.messaging.ReliableMessage;
import net.sourceforge.peertwitter.core.applications.communication.messaging.ReliableMessageEnvelope;
import net.sourceforge.peertwitter.core.applications.storage.MBStorage;
import net.sourceforge.peertwitter.core.events.MessageDelivery;
import net.sourceforge.peertwitter.core.events.NodeJoin;
import net.sourceforge.peertwitter.core.events.NodeLeave;
import net.sourceforge.peertwitter.core.messaging.TweetMessage;
import net.sourceforge.peertwitter.simulation.MessagePool;
import net.sourceforge.peertwitter.simulation.NullMessagePool;

import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;

import rice.environment.Environment;
import rice.p2p.commonapi.Endpoint;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.Message;
import rice.p2p.commonapi.Node;
import rice.p2p.commonapi.NodeHandle;
import rice.p2p.commonapi.rawserialization.OutputBuffer;

public class PeerTwitterImplTest {

	private Mockery context;
	private Environment environment;
	private MessagePool messagePool;
	private Node node;
	private Profile profile;
	private Endpoint endpoint;
	private Id id;
	private Message message;
	private MBStorage storage;
	private MBGroupCommunication groupCommunication;
	private NodeHandle nodeHandle;

	@Before
	public void createInfrastructure() {
		context = new Mockery();
		environment = Environment.directEnvironment();
		messagePool = context.mock(MessagePool.class);
		id = rice.pastry.Id.build("0xffabcdeffffffffffffffffffffff");
		profile = new Profile(id);
		node = context.mock(Node.class);
		nodeHandle = new NodeHandle() {

			private static final long serialVersionUID = -7820576780932890883L;

			@Override
			public boolean checkLiveness() {
				return true;
			}

			@Override
			public Id getId() {
				return id;
			}

			@Override
			public boolean isAlive() {
				return true;
			}

			@Override
			public int proximity() {
				return 0;
			}

			@Override
			public void serialize(OutputBuffer arg0) throws IOException {
				
			}
			
		};
		endpoint = context.mock(Endpoint.class);
		message = context.mock(Message.class);
		storage = context.mock(MBStorage.class);
		groupCommunication = context.mock(MBGroupCommunication.class);
	}
	
	
	@Test
	public void peerTwitterIsCreatedWithTheRightIdAndProfile() {
		setBasicExpectations();
		PeerTwitterImpl peerTwitter = new PeerTwitterImpl(environment, node, messagePool, profile);
		
		context.assertIsSatisfied();
		assertEquals(profile.getId(), peerTwitter.getId());
		assertEquals(profile, peerTwitter.getProfile());
	}

	
	@Test
	public void whenPeerTwitterIsCreatedThereMustBeABuildPointCreationAndRegistration() {
		setBasicExpectations();
		new PeerTwitterImpl(environment, node, messagePool, profile);
		
		context.assertIsSatisfied();
	}

	@Test
	public void whenPeerTwitterReceivesADeliverItMustPassItOnToChildrenApplications() {
		setBasicExpectations();
		context.checking(new Expectations() {
			{
				oneOf(groupCommunication).deliver(new MessageDelivery(id, message));
				oneOf(storage).deliver(new MessageDelivery(id, message));
				oneOf(messagePool).deliver(new MessageDelivery(id, message));
			}
		});
		PeerTwitterImpl peerTwitter = new PeerTwitterImpl(environment, node, messagePool, profile);
		peerTwitter.setMBStorage(storage);
		peerTwitter.setMBGroupCommunication(groupCommunication);
		peerTwitter.deliver(id, message);
		context.assertIsSatisfied();
	}

	@Test
	public void whenPeerTwitterVerifiesANodeJoinItShouldPassItOnToChildrenApplication() {
		setBasicExpectations();
		context.checking(new Expectations() {
			{
				oneOf(groupCommunication).deliver(new NodeJoin(nodeHandle));
				oneOf(storage).deliver(new NodeJoin(nodeHandle));
				oneOf(messagePool).deliver(new NodeJoin(nodeHandle));
			}
		});
		PeerTwitterImpl peerTwitter = new PeerTwitterImpl(environment, node, messagePool, profile);
		peerTwitter.setMBStorage(storage);
		peerTwitter.setMBGroupCommunication(groupCommunication);
		peerTwitter.update(nodeHandle, true);
		context.assertIsSatisfied();
	}
	
	@Test
	public void whenPeerTwitterVerifiesANodeLeaveItShouldPassItOnToChildrenApplication() {
		setBasicExpectations();
		context.checking(new Expectations() {
			{
				oneOf(groupCommunication).deliver(new NodeLeave(nodeHandle));
				oneOf(storage).deliver(new NodeLeave(nodeHandle));
				oneOf(messagePool).deliver(new NodeLeave(nodeHandle));
			}
		});
		PeerTwitterImpl peerTwitter = new PeerTwitterImpl(environment, node, messagePool, profile);
		peerTwitter.setMBStorage(storage);
		peerTwitter.setMBGroupCommunication(groupCommunication);
		peerTwitter.update(nodeHandle, false);
		context.assertIsSatisfied();
	}
	@Test
	public void aTweetInvocationCallsGroupCommunicationReliableSend() {
		setBasicExpectations();
		long aTimeStamp = environment.getTimeSource().currentTimeMillis();
		TweetMessage tm = new TweetMessage(nodeHandle, aTimeStamp, "message text");
		final ReliableMessage reliableEnvelopedTweet = new ReliableMessageEnvelope(tm);
		
		context.checking(new Expectations() {
			{
				oneOf(groupCommunication).sendReliable(with(id), with(equal(reliableEnvelopedTweet)));
			}
		});
		
		PeerTwitterImpl peerTwitter = new PeerTwitterImpl(environment, node, new NullMessagePool(), profile);
		peerTwitter.setMBGroupCommunication(groupCommunication);
		peerTwitter.tweet("message text");
		context.assertIsSatisfied();
	}
	
	@Test
	public void aTweetInvocationRegisterMessageOnMessagePool() {
		setBasicExpectations();
		long aTimeStamp = environment.getTimeSource().currentTimeMillis();
		final TweetMessage tm = new TweetMessage(nodeHandle, aTimeStamp, "message text");
		final ReliableMessage reliableEnvelopedTweet = new ReliableMessageEnvelope(tm);
		
		context.checking(new Expectations() {
			{
				oneOf(messagePool).register(tm);
				oneOf(groupCommunication).sendReliable(with(id), with(equal(reliableEnvelopedTweet)));
			}
		});
		PeerTwitterImpl peerTwitter = new PeerTwitterImpl(environment, node, messagePool, profile);
		peerTwitter.setMBGroupCommunication(groupCommunication);
		peerTwitter.tweet("message text");
		context.assertIsSatisfied();
	}
	
	
	public void aUpdateShouldCallMBPastAndScampUpdate() {
		setBasicExpectations();
		context.checking(new Expectations() {
			{
				oneOf(groupCommunication);
			}
		});
		
	}
	private void setBasicExpectations() {
		context.checking(new Expectations() { 
			{
				allowing(node).getId();
				will(returnValue(profile.getId()));
				
				allowing(node).getLocalNodeHandle();
				will(returnValue(nodeHandle));
				
				oneOf(node).buildEndpoint(with(any(PeerTwitterImpl.class)), with(PeerTwitterImpl.APPLICATION_NAME));
				will(returnValue(endpoint));
				
				oneOf(endpoint).register();
			}
		});
	}
}
